home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
news
/
suck-2.6
/
suck-2
/
suck-2.6.3
/
killfile.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-05-07
|
22KB
|
769 lines
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include "config.h"
#include "both.h"
#include "suck.h"
#include "suckutils.h"
#include "killfile.h"
#ifdef TIMER
#include "timer.h"
#endif
#define PATHHOST_SEPARATOR ',' /* character used to separate hosts in the pathhost line in killfile */
#define SUBJECT_SEPARATOR ',' /* character used to separate words in the subject line in killfile */
#define FROM_SEPARATOR ',' /* character used to separate names in the subject line in killfile */
#define GROUP_KEEP "keep"
#define GROUP_DELETE "delete" /* word in param file parsed for on group line to signify keep or delete */
#define COMMA ',' /* group separator in newsgroup line in article */
/* local function prototypes */
char *get_a_chunk_mem(PMaster);
int chk_msg_kill(PKillStruct, char *);
char *malloc_line(int which, char *linein);
int check_line(char *, char *, char *, int, int, char);
void free_node(OneKill);
int parse_a_file(char *, char *, POneKill);
int pass_two(PKillStruct);
int check_a_group(POneKill, char *, int *);
char *strnstr(char *, char *);
/*-------------------------------------------------------------------------*/
const char *Reasons[] = {
"No Reason - major screwup",
"Over Hi-Line paramter",
"Under Low-Line parameter",
"Host in Path rejected",
"Word in Subject rejected",
"Name in From rejected",
"Not matched in Group Keep file",
"Multiple Matches/Tiebreaker Used"
};
enum { REASON_NONE, REASON_TOOMANYLINES, REASON_NOTENUFLINES, REASON_PATHHOST,
REASON_SUBJECT, REASON_FROM, REASON_NRGRPS, REASON_NOKEEP, REASON_TIE};
enum { CHECK_EXACT, CHECK_CHECK };
const struct {
int len;
char *name;
int headerlen;
char *header;
} Params[] = {
{8, "HILINES=", 7, "Lines: " },
{9, "LOWLINES=", 7, "Lines: "},
{5, "PATH=", 6, "Path: " },
{8, "SUBJECT=", 9, "Subject: " },
{5, "FROM=", 6, "From: " },
{7, "NRGRPS=", 12, "Newsgroups: "},
{6, "GROUP=", 0, ""},
{6, "QUOTE=", 0, ""}
};
enum { PARAM_HILINE, PARAM_LOWLINE, PARAM_PATH, PARAM_SUBJ, PARAM_FROM, PARAM_NRGRPS, PARAM_GROUP, PARAM_QUOTE};
#define NR_PARAMS (sizeof(Params)/sizeof(Params[0]))
enum { DELKEEP_KEEP, DELKEEP_DELETE };
/*--------------------------------------------------------------------------*/
PKillStruct parse_killfile() {
static KillStruct mykill;
PKillStruct retptr;
FILE *fptr;
char buf[MAXLINLEN+1];
/* first initialize it */
mykill.nrgrps = 0;
mykill.grps = NULL;
retptr = &mykill;
/* kill file is going to get three passes, 1st one to count how many group files to process */
/* so can allocate memory for all the group stuff. */
/* 2nd pass will be to actually process the group files */
/* 3rd pass will be to process the master delete stuff */
/* FIRST PASS THRU MASTER KILLFILE - only look for group delete/keeps and count em */
if((fptr = fopen(full_path(FP_GET, FP_DATADIR, N_KILLFILE), "r")) == NULL) {
/* MyPerror(full_path(FP_GET, FP_DATADIR, N_KILLFILE)); */
/* this is not really an error, so don't report it as such */
retptr = NULL;
}
else {
#ifdef DEBUG2
do_debug("Pass 1 kill file: %s\n", full_path(FP_GET, FP_DATADIR, N_KILLFILE));
#endif
while(fgets(buf, MAXLINLEN, fptr) != NULL) {
#ifdef DEBUG2
do_debug("Read kill file line: %s\n", buf);
#endif
if(strncmp(buf, Params[PARAM_GROUP].name, Params[PARAM_GROUP].len) == 0) {
mykill.nrgrps++; /* how many group files must we process */
}
}
fclose(fptr);
/* SECOND PASS - call routine */
if(mykill.nrgrps > 0) {
if(pass_two(&mykill) == RETVAL_ERROR) {
retptr = NULL;
}
}
/* THIRD PASS - process master delete stuff */
if(retptr != NULL && parse_a_file(N_KILLFILE, "Master", &(mykill.master)) != RETVAL_OK) {
retptr = NULL;
}
}
if(retptr == NULL) {
free_killfile(&mykill); /* just in case any memory got allocated */
}
return retptr;
}
/*-------------------------------------------------------------------------------------------*/
int pass_two(PKillStruct killp ) {
int retval = RETVAL_OK;
FILE *fptr;
char buf[MAXLINLEN];
int i, grpon = 0;
char *grpname, *grpfile, *delkeep;
grpname = grpfile = delkeep = NULL;
/* SECOND PASS - now that we know how many, we can allocate the space for em */
/* and then have parse_a_file read em in. */
if((killp->grps = calloc(killp->nrgrps, sizeof(Group))) == NULL) {
retval = RETVAL_ERROR;
error_log(ERRLOG_REPORT, "Out of memory, can't do killfiles\n");
}
else if((fptr = fopen(full_path(FP_GET, FP_DATADIR, N_KILLFILE), "r")) == NULL) {
MyPerror(full_path(FP_GET, FP_DATADIR, N_KILLFILE));
retval = RETVAL_ERROR;
}
else {
#ifdef DEBUG2
do_debug("Pass 2 kill file: %s\n", full_path(FP_GET, FP_DATADIR, N_KILLFILE));
#endif
while(retval == RETVAL_OK && fgets(buf, MAXLINLEN, fptr) != NULL) {
#ifdef DEBUG2
do_debug("Read kill file line: %s\n", buf);
#endif
if(strncmp(buf, Params[PARAM_GROUP].name, Params[PARAM_GROUP].len) == 0 ) {
/* now parse the line for the 3 required elements */
/* keep/delete group_name filename */
delkeep = &buf[Params[PARAM_GROUP].len];
if(strncmp(delkeep, GROUP_KEEP, strlen(GROUP_KEEP)) == 0) {
killp->grps[grpon].delkeep = DELKEEP_KEEP;
}
else if(strncmp(delkeep, GROUP_DELETE, strlen(GROUP_DELETE)) == 0) {
killp->grps[grpon].delkeep = DELKEEP_DELETE;
}
else {
retval = RETVAL_ERROR;
}
if(retval == RETVAL_OK) {
grpname = strchr(delkeep, ' '); /* find the space */
if(grpname == NULL) {
retval = RETVAL_ERROR;
}
else {
++grpname; /* move past space */
grpfile = strchr(grpname, ' ');
if(grpfile == NULL) {
retval = RETVAL_ERROR;
}
else {
*grpfile = '\0'; /* truncate the group name for easier copying later */
++grpfile;
}
/* nuke newline */
i = strlen(grpfile) - 1;
if(grpfile[i] == '\n') {
grpfile[i] = '\0';
}
}
}
if(retval == RETVAL_ERROR) {
error_log(ERRLOG_REPORT, "Invalid Parameter Line: %s\n", buf);
}
else { /* have all three params, put them in place and parse the file */
if((killp->grps[grpon].group = malloc(strlen(grpname)+1)) == NULL) {
error_log(ERRLOG_REPORT, "Out of Memory");
retval = RETVAL_ERROR;
}
else {
strcpy(killp->grps[grpon].group, grpname);
retval = parse_a_file(grpfile, grpname, &(killp->grps[grpon].match));
}
}
grpon++; /* finished with this group */
}
}
fclose(fptr);
}
return retval;
}
/*--------------------------------------------------------------------------*/
void free_killfile(PKillStruct master) {
int i;
if(master != NULL) {
free_node(master->master);
if(master->nrgrps > 0) {
for(i=0;i<master->nrgrps;i++) {
free_node(master->grps[i].match);
free(master->grps[i].group);
}
free(master->grps);
}
if(master->master.path != NULL) {
free(master->master.path);
}
}
}
/*--------------------------------------------------------------------*/
void free_node(OneKill node) {
if(node.path != NULL) {
free(node.path);
}
if(node.from != NULL) {
free(node.from);
}
if(node.subj != NULL) {
free(node.subj);
}
}
/*--------------------------------------------------------------------------*/
int get_one_article_kill(PMaster master, int logcount, PKillStruct killp) {
char buf[MAXLINLEN+1], *inbuf, *fname;
int retval;
FILE *fptr;
retval = RETVAL_OK;
/* build command to get article header*/
sprintf(buf, "head %s\r\n", (master->curr)->msgnr);
switch(send_a_command(master->sockfd, buf, 221)) {
case RETVAL_ERROR:
retval = RETVAL_ERROR;
break;
case RETVAL_OK:
if((inbuf = get_a_chunk_mem(master)) == NULL) {
retval = RETVAL_ERROR;
}
else if(chk_msg_kill(killp, inbuf)==FALSE){
if(master->MultiFile == TRUE) {
/* open file */
/* file name will be ####-#### ex 001-166 (nron,total) */
sprintf(buf,"%0*d-%d", logcount, master->itemon, master->nritems);
fname = full_path(FP_GET, FP_MSGDIR, buf);
if((fptr = fopen(fname, "w")) == NULL) {
MyPerror(fname);
retval = RETVAL_ERROR;
}
else {
fputs(inbuf, fptr);
fputs("\n", fptr); /* needed */
sprintf(buf, "body %s\r\n", (master->curr)->msgnr);
switch(send_a_command(master->sockfd, buf, 222)) {
case RETVAL_OK:
retval = get_a_chunk(master->sockfd, fptr);
break;
case RETVAL_ERROR:
retval = RETVAL_ERROR;
break;
case RETVAL_UNEXPECTEDANS:
break;
}
fclose(fptr);
if(retval != RETVAL_OK) {
unlink(fname);
}
}
}
else {
fputs(inbuf, stdout);
fputs("\n", stdout);
sprintf(buf, "body %s\r\n", (master->curr)->msgnr);
switch(send_a_command(master->sockfd, buf, 222)) {
case RETVAL_OK:
retval = get_a_chunk(master->sockfd, stdout);
/* this is needed as a separator */
/* in stdout version */
fputs(".\n", stdout);
break;
case RETVAL_ERROR:
retval = RETVAL_ERROR;
break;
case RETVAL_UNEXPECTEDANS:
/* nothing to do */
break;
}
}
if(retval == RETVAL_OK) {
master->nrgot++;
}
}
break;
case RETVAL_UNEXPECTEDANS:
break;
}
return retval;
}
/*---------------------------------------------------------------*/
/* this routine is same as get_a_chunk() except it goes to memory */
char *get_a_chunk_mem(PMaster master) {
static char *buf = NULL;
static int bufsize = 8192;
int retval, done, partial, len, currbuf;
char *inbuf, *newbuf;
retval = RETVAL_OK;
done = FALSE;
partial = FALSE;
currbuf = 0;
if(buf == NULL) {
#ifdef DEBUG2
do_debug("allocing memory, size = %d\n", bufsize);
#endif
if((buf=malloc((size_t) bufsize)) == NULL) {
error_log(ERRLOG_REPORT, "Out of Memory");
}
}
while(buf != NULL && done == FALSE) {
len=sgetline(master->sockfd, &inbuf);
#ifdef TIMER
TimerFunc(TIMER_ADDBYTES, len);
#endif
if(len < 0) {
free(buf);
buf = NULL;
done = TRUE;
}
else if(partial == FALSE && inbuf[0] == '.') {
if(len == 2 && inbuf[1] == '\n') {
done = TRUE;
}
else if(master->MultiFile == TRUE) {
/* handle double dots IAW RFC977 2.4.1*/
/* don't do if we aren't doing multifile, since */
/* stdout needs the .. to distinguish dots and EOM */
inbuf++; /* move past first dot */
len--;
}
}
if(done == FALSE) {
if((len+currbuf) > bufsize) {
/* buffer not big enough realloc */
bufsize = len+currbuf;
if((newbuf = realloc(buf, (size_t) bufsize)) == NULL) {
free(buf);
buf = NULL;
error_log(ERRLOG_REPORT, "Out of Memory");
#ifdef DEBUG2
do_debug("Re-alloc failed\n");
#endif
}
else {
buf = newbuf;
#ifdef DEBUG2
do_debug("Re-alloc succeeded, new size = %d\n", bufsize);
#endif
}
}
if(buf != NULL) {
strcpy(buf+currbuf, inbuf);
currbuf += len;
partial= (len==MAXLINLEN&&inbuf[len-1]!='\n') ? TRUE : FALSE;
}
}
}
return buf;
}
/*-------------------------------------------------------------------------*/
/* chk_msg_kill - return TRUE if kill article, FALSE if keep */
/* if kill article, add it to killlog */
int chk_msg_kill(PKillStruct killp, char *headerbuf) {
int why, goodwhy, killyn, i, del, keep, match;
char *group = "Master";
#ifdef KILLFILE_LOG
FILE *fptr;
#endif
goodwhy = why = REASON_NONE;
/* first check against master delete */
if((killyn = check_a_group(&(killp->master), headerbuf, &why)) == FALSE) {
/* okay now have to parse group line */
/* then check to see if I have group keep/deletes for each group */
/* default actions */
keep = FALSE;
del = FALSE;
for(i=0;i<killp->nrgrps;i++) {
if(check_line(headerbuf, "Newsgroups: ", killp->grps[i].group, ',', CHECK_EXACT, ' ') == TRUE) {
/* bingo this article matches one of our group check it */
match = check_a_group(&(killp->grps[i].match), headerbuf, &why);
if(killp->grps[i].delkeep == DELKEEP_KEEP) {
/* matched keep group */
if(match == TRUE) {
keep = TRUE;
}
else {
del = TRUE;
group = killp->grps[i].group;
goodwhy = REASON_NOKEEP;
}
}
else {
if(match == TRUE) {
del = TRUE;
goodwhy = why;
group = killp->grps[i].group;
}
else {
keep = TRUE;
}
}
}
}
/* now determine if we kill or keep this sucker */
if(keep == FALSE && del == FALSE) {
/* no group matches, keep it */
killyn = FALSE;
}
else if(keep != del) {
/* only matched one group, figure out which */
killyn = ( del == TRUE) ? TRUE : FALSE;
why = goodwhy;
}
else {
/* matched both, use TIEBREAKER */
why = REASON_TIE;
#ifdef KILL_TIEBREAKER_KEEP
killyn = FALSE;
#else
killyn = TRUE;
#endif
}
}
#ifdef KILLFILE_LOG
if(killyn == TRUE) {
/* log it */
if((fptr = fopen(full_path(FP_GET, FP_TMPDIR, N_KILLLOG), "a")) == NULL) {
MyPerror("Unable to log killed article");
}
else {
fprintf(fptr, "ARTICLE KILLED: %s - %s\n%s\n", group, Reasons[why], headerbuf);
fclose(fptr);
}
}
#endif
return killyn;
}
/*-----------------------------------------------------------------------*/
int check_a_group(POneKill killp, char *headerbuf, int *why) {
int i, match = FALSE;
char *startline, *tptr;
/* check hilines first */
if(killp->hilines > 0) {
if((startline = strstr(headerbuf, Params[PARAM_HILINE].header)) != NULL) {
i = 0; /* just in case */
sscanf(startline+Params[PARAM_HILINE].headerlen, "%d", &i);
#ifdef DEBUG2
do_debug("Article has %d lines, hilines = %d\n", i, killp->hilines);
#endif
if(killp->hilines < i) {
/* JACKPOT */
match = TRUE;
*why = REASON_TOOMANYLINES;
}
}
}
/* now check low lines */
if(match == FALSE && killp->lowlines > 0) {
if((startline = strstr(headerbuf, Params[PARAM_LOWLINE].header)) != NULL) {
i = 0; /* just in case */
sscanf(startline+Params[PARAM_LOWLINE].headerlen, "%d", &i);
#ifdef DEBUG2
do_debug("Article has %d lines, lowlines = %d\n", i, killp->lowlines);
#endif
if(i < killp->lowlines) {
/* JACKPOT */
match = TRUE;
*why = REASON_NOTENUFLINES;
}
}
}
/* now check nrgrps */
if(match == FALSE && killp->nrgrps > 0) {
if((startline = strstr(headerbuf, Params[PARAM_NRGRPS].header)) != NULL) {
/* count the nr of commas in the group line */
i = 1; /* have at least one group */
tptr = startline;
while(i <= killp->nrgrps && *tptr != '\0' ) {
if(*tptr++ == COMMA) {
i++;
}
}
if(i >= killp->nrgrps) {
match = TRUE;
*why = REASON_NRGRPS;
}
}
}
/* check host */
if(match == FALSE && killp->path != NULL) {
if(check_line(headerbuf, Params[PARAM_PATH].header, killp->path, PATHHOST_SEPARATOR, CHECK_CHECK, killp->quote) == TRUE) {
/* jackpot */
match = TRUE;
*why = REASON_PATHHOST;
}
}
/* check from */
if(match == FALSE && killp->from != NULL) {
if(check_line(headerbuf, Params[PARAM_FROM].header, killp->from, FROM_SEPARATOR, CHECK_CHECK, killp->quote) == TRUE) {
/* jackpot */
match = TRUE;
*why = REASON_FROM;
}
}
/* check subject */
if(match == FALSE && killp->subj != NULL) {
if(check_line(headerbuf, Params[PARAM_SUBJ].header, killp->subj, SUBJECT_SEPARATOR, CHECK_CHECK, killp->quote) == TRUE) {
/* jackpot */
match = TRUE;
*why = REASON_SUBJECT;
}
}
return match;
}
/*-----------------------------------------------------------------------*/
char *malloc_line(int which, char *linein) {
/* malloc and copy a header line WITHOUT the header field */
char *ptr;
int x;
/* len = how much not to malloc */
if((ptr = malloc((strlen(linein)+1) - Params[which].len)) == NULL) {
error_log(ERRLOG_REPORT, "Unable to malloc memory\n");
}
else {
strcpy(ptr, &linein[Params[which].len]);
/* strip the newline, if present */
x = strlen(ptr);
if(ptr[x-1] == '\n') {
ptr[x-1] = '\0';
}
}
return ptr;
}
/*--------------------------------------------------------------------------*/
int check_line(char *header, char *whichline, char *linematch, int separator, int check_quote, char quote_char) {
/* search for each item in linematch on whichline in the header */
/* if check_quote = CHECK_CHECK check if first char is char_quote */
/* if it is, then do strcmp, else do a strncmp */
/* if check_quote = CHECK_EXACT don't check first char just strcmp */
char *startline, *endline, *currcomma, *nextcomma;
int casecmp, match = FALSE;
if((startline = strstr(header, whichline)) != NULL) {
currcomma = linematch; /* start us off */
endline = strchr(startline, '\n');
/* end this line, so we only search the right header line */
if(endline != NULL) {
*endline = '\0';
}
do {
nextcomma = strchr(currcomma, separator);
if(nextcomma != NULL) {
*nextcomma = '\0'; /* null terminate current entry in linematch */
}
#ifdef DEBUG2
do_debug("Checking %s for \"%s\"\n", whichline, currcomma);
#endif
/* now set the case comparison flag for this string */
casecmp = (check_quote == CHECK_EXACT) ? TRUE : FALSE;
if(check_quote == CHECK_CHECK) {
if(*currcomma == quote_char) {
casecmp = TRUE;
currcomma++;
}
}
if(casecmp == TRUE) {
if(strstr(startline, currcomma) != NULL) {
/* jackpot */
match = TRUE;
}
}
else if(strnstr(startline, currcomma) != NULL) {
match = TRUE;
}
if(nextcomma != NULL) {
*nextcomma = separator; /* must restore */
currcomma = nextcomma+1; /* check next +1 to move past comma */
}
}
while(nextcomma != NULL && match == FALSE);
if(endline != NULL) { /* restore previously nuked nl */
*endline ='\n';
}
}
return match;
}
/*-------------------------------------------------------------------------------*/
int parse_a_file(char *fname, char *group, POneKill mykill) {
FILE *fptr;
char buf[MAXLINLEN+1];
int i;
int retval = RETVAL_OK;
/* first initialized the killstruct */
mykill->hilines = mykill->lowlines = mykill->nrgrps = 0;
mykill->path = mykill->from = mykill->subj = NULL;
mykill->quote = KILLFILE_QUOTE;
/* now read in the killfile and parse it */
if((fptr = fopen(full_path(FP_GET, FP_DATADIR, fname), "r")) == NULL) {
MyPerror(full_path(FP_GET, FP_DATADIR, fname));
retval = RETVAL_ERROR;
}
else {
#ifdef DEBUG2
do_debug("Opening kill file: %s\n", full_path(FP_GET, FP_DATADIR, fname));
#endif
while(fgets(buf, MAXLINLEN, fptr) != NULL) {
#ifdef DEBUG2
do_debug("Read kill file line: %s\n", buf);
#endif
buf[MAXLINLEN] = 0; /* just in case */
for(i = 0 ; i < NR_PARAMS; i++) {
if(strncmp(buf, Params[i].name, Params[i].len) == 0) {
switch(i) {
case PARAM_HILINE:
sscanf(&buf[Params[PARAM_HILINE].len], "%d", &(mykill->hilines));
#ifdef DEBUG2
do_debug("Killfile hilines = %d\n", mykill->hilines);
#endif
break;
case PARAM_LOWLINE:
sscanf(&buf[Params[PARAM_LOWLINE].len], "%d", &(mykill->lowlines));
#ifdef DEBUG2
do_debug("Killfile lowlines = %d\n", mykill->lowlines);
#endif
break;
case PARAM_NRGRPS:
sscanf(&buf[Params[PARAM_NRGRPS].len], "%d", &(mykill->nrgrps));
#ifdef DEBUG2
do_debug("Killfile nrgrps = %d\n", mykill->nrgrps);
#endif
break;
case PARAM_PATH:
if((mykill->path = malloc_line(PARAM_PATH, buf)) == NULL) {
error_log(ERRLOG_REPORT, "No paths will be processed for %s\n", group);
}
#ifdef DEBUG2
else {
do_debug("Killfile path = %s\n", mykill->path);
}
#endif
break;
case PARAM_FROM:
if((mykill->from = malloc_line(PARAM_FROM, buf)) == NULL) {
error_log(ERRLOG_REPORT, "No From lines will be processed for %s\n", group);
}
#ifdef DEBUG2
else {
do_debug("Killfile from = %s\n", mykill->from);
}
#endif
break;
case PARAM_SUBJ:
if((mykill->subj = malloc_line(PARAM_SUBJ, buf)) == NULL) {
error_log(ERRLOG_REPORT, "No Subjects will be processed for %s\n", group);
}
#ifdef DEBUG2
else {
do_debug("Killfile Subject = %s\n", mykill->subj);
}
#endif
break;
case PARAM_QUOTE:
if(buf[Params[PARAM_QUOTE].len] == '\0' && buf[Params[PARAM_QUOTE].len] == '\n') {
error_log(ERRLOG_REPORT, "No character to process for quote, ignoring\n");
}
else {
mykill->quote = buf[Params[PARAM_QUOTE].len];
#ifdef DEBUG2
do_debug("Killfile Quote = '%c'\n", mykill->quote);
#endif
}
break;
default: /* ignore group lines, those processed elsewhere */
break;
}
}
}
}
fclose(fptr);
}
return retval;
}
/*---------------------------------------------------------------------------------------------*/
char *strnstr(char *linein, char *matchstr) {
/* see if matchstr exists in inline, case insensitive */
/* return start of string in inline, else return NULL */
char *tptr, *startchar, *retstr = NULL;
if(linein != NULL && matchstr != NULL) {
while(*linein != '\0' && retstr == NULL) {
/* first see if I can find the first char */
while(*linein != '\0' && tolower(*linein) != tolower(*matchstr)) {
linein++;
}
if(*linein != '\0') {
/* bingo */
startchar = linein;
/* now try to match rest of string */
tptr = matchstr;
while(tolower(*startchar) == tolower(*tptr) && *tptr != '\0') {
startchar++;
tptr++;
}
if(*tptr == '\0') {
/* bingo, we have a match */
retstr = linein;
}
else {
/* no match */
linein++;
}
}
}
}
return retstr;
}